#include <sys/types.h>
#include <sys/wait.h>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <unistd.h>
#include <cstring>
#include <ctype.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace std;


#define TRIM_SPACE(target) do{   \
    if(isspace(*target)){       \
        ++target;               \
    }                           \
}while(0)

#define nr_g_arg  64
const int basesize = 1024;
const int nr_env = 64;
char *command;



char *g_argv[nr_g_arg];
int g_argc;

// 全局的当前工作路径
char pwd[basesize];
char pwdPattern[basesize];
// 全局的上一次退出码
int lastcode = 0;
// 我的系统的环境变量
char *g_env[nr_env];
// 重定向相关
#define NoneRedir   0
#define InputRedir  1
#define OutputRedir 2
#define AppendRedir 3
int redir_tag = NoneRedir;
char *filename = nullptr;


const char* GetHost(){
    static string host = getenv("HOSTNAME");
    return host.empty() ? "None" : host.c_str();
}

const char* GetUsr(){
    static string usr = getenv("USER");
    return usr.empty() ? "None" : usr.c_str();
}

const char* GetPwd(){
    if(nullptr == getcwd(pwd, sizeof(pwd))){
        return "None";
    }	    
    snprintf(pwdPattern,sizeof(pwdPattern),"PWD=%s", pwd);
    putenv(pwdPattern);
    return pwd; 
}

const char* LastDir(const char* path){
    if(path == nullptr||0==strcmp(path,"/")) return "None";
    size_t sz = strlen(path);
    for(int i = sz; i >= 0; --i){
	if(path[i] == '/'){
	    return path + i + 1;
	}
    }
    return "None";
}

void PrintCommandPrompt(){
    const char* usr = GetUsr();
    const char* host = GetHost();
    //const char* pwd = GetPwd();
    char CommandLine[basesize];
    // 和bash做区分
    snprintf(CommandLine, basesize, "[%s@%s %s]! ",GetUsr(),host,LastDir(GetPwd()));
    printf("%s",CommandLine);
    fflush(stdout);
}
bool GetCommand(){
    command = (char*)malloc(sizeof(char)*basesize);
    
    if(nullptr != fgets(command, basesize, stdin)){
        //读取后包含了\n
        if(strlen(command)==1) return false;
        command[strlen(command)-1] = '\0';//替换为 0
        return true;
    }
    return false;
}
void Reset(){
    memset(g_argv, 0, sizeof(g_argv));   
    g_argc = 0;
    redir_tag = NoneRedir;
    filename = nullptr;
}
void DoRedir(){
    // 重定向
    //printf("command : %s\n", command);
    //printf("Parse redirection start\n");
    // 重定向的分析
    int len = strlen(command);
    int end = len - 1;
    while(end >= 0){
        // 输入重定向
        // 将本应该输入到显示器的改到其他文件中
        if(command[end] == '<'){
            redir_tag = InputRedir;
            filename = command + end + 1;
            TRIM_SPACE(filename);
            command[end] = 0;
            break;
        }else if(command[end] == '>'){
            // 追加重定向
            if(command[end - 1] == '>'){
                redir_tag = AppendRedir;
                filename = command + end + 1;
                TRIM_SPACE(filename);
                command[end - 1] = 0;
                break;
            }else {
            // 输出重定向
                redir_tag = OutputRedir;
                filename = command + end + 1;
                TRIM_SPACE(filename);
                command[end] = 0;
                break;
            }
        }
        // 找到了关键字符就只分一次，没有匹配的就 end--;
        else{
            --end;
        }
    }
    //printf("Parse redirection end\n");
    //printf("redir_tag : %d\n", redir_tag);
    //printf("command : %s\n", command);
    //printf("filename : %s\n", filename);
    
}
void DoCommand(){
    int i = 0;
    const char* delimiter = " ";
    g_argv[i++] = strtok(command,delimiter);
    while((bool)(g_argv[i++]=strtok(nullptr,delimiter)));
    i--;
    g_argc = i;
}
void ParseCommand(){
    Reset();
    DoRedir();
    DoCommand();
}

void debug(){
    printf("g_argc : %d\n", g_argc);	
    for(int i = 0; g_argv[i]; ++i){
    	printf("%s ",g_argv[i]);
    }   
}
void ExecuteRedir(){
    if(redir_tag == InputRedir) 
    {
        if(filename){
            int fdInput = open(filename, O_RDONLY);
            if(fdInput < 0){exit(2);}
            dup2(fdInput, 0);
        }else
        { 
            exit(1);
        }
    }
    else if(redir_tag == OutputRedir){
        if(filename){
            int fdOutput = open(filename, O_CREAT|O_TRUNC|O_WRONLY , 0666);
            if(fdOutput < 0){exit(3);}
            dup2(fdOutput, 1);
        }
        else
        {
            exit(1);
        }
    }
    else if(redir_tag == AppendRedir){
        if(filename){
            int fdAppend = open(filename, O_CREAT|O_WRONLY|O_APPEND, 0666);
            if(fdAppend < 0){exit(4);}
            dup2(fdAppend, 1);
        }
        else {
            exit(1);
        }
    }
    else{/*donothing*/}

}

bool ExecuteCommand(){
    pid_t id = fork();
    if(id < 0) return false;
    if(id == 0){
        ExecuteRedir();
        //子进程
    	execvpe(g_argv[0], g_argv, g_env);
	    //程序替换失败
	    exit(1);
    }
    //父进程
    else if(id > 0){
	    int status = 0;
	    pid_t rid = waitpid(id, &status, 0);
	if(rid > 0)
	{
	    if(WIFEXITED(status)){
	        lastcode = WEXITSTATUS(status); 
	    }	    
	    else{
		    lastcode = 100;
	    }
	    return true;
	}
	else {return false;}
    }
    else{
	    return false;
    }
}


void InitEnv(){
    //将 bash 的环境变量引入到 自己的 shell中
    extern char** environ;
    int i = 0; 
    for(i; environ[i]; ++i){
        g_env[i] = (char*)malloc(strlen(environ[i])+1);
	// strncpy(g_env[i], environ[i],strlen(environ[i]+1));
        memcpy(g_env[i], environ[i],strlen(environ[i]+1));
    }
    g_env[i] = nullptr;
}
void AddEnv(const char *item){
    int index = 0;
    while(g_env[index]){index++;}
    
    g_env[index] = (char*)malloc(strlen(item)+1);
    strncpy(g_env[index], item, strlen(item)+1);
    g_env[++index] = nullptr;
}

bool CheckBuiltInCommand(){
    //对于 内建命令 不需要也不能让子进程运行
    //只有外部命令需要子进程，我父进程管理好就行了
    if(strcmp(g_argv[0], "cd") == 0){
        if(2 == g_argc){
            chdir(g_argv[1]);
	}
	else { lastcode = 1;}
        return true;
    }
    else if(0==strcmp(g_argv[0], "export")){
        if(2 == g_argc){
	     AddEnv(g_argv[1]);
	}
	else { lastcode = 2;}
	return true;    
    }
    else if(0 == strcmp(g_argv[0], "env")){
	for(int i = 0; g_env[i]; ++i){
	    printf("%s\n",g_env[i]);
	}
	lastcode = 0;
	return true;
    }
    else if(0 == strcmp(g_argv[0], "echo")){
	if(2 == g_argc){
	    if(g_argv[1][0] == '$'){
	        if(g_argv[1][1] == '?'){
		    printf("%d\n", lastcode);
		}
		else{
		    /*if(!check()){
		        lastcode = 4;
			return false;
		    }
		    printf("%s\n", )*/
		}
	    }
	    else{
		printf("%s\n",g_argv[1]);
	    }
	    lastcode = 0;
	    return true;
			    
        }
	else{
	    lastcode = 3;
	    return false;
	}

    }
    return false;
}
int main(){
    // 模拟bash将配置文件读取到环境变量中
    InitEnv();
    while(true){
	//命令行+命令行提示符 格式
	PrintCommandPrompt();
	//用户输入命令，将其给到全局变量command
	if(!GetCommand()){
             continue;		
	}

	//分析用户指令，将指令分好词块
	ParseCommand();
	//debug();
        if(CheckBuiltInCommand()){continue;}
        ExecuteCommand(); 
    }


    return 0;
}
